package ru.mcfr.oxygen.framework.filters;

import org.apache.log4j.Logger;
import ro.sync.ecss.extensions.api.*;
import ro.sync.ecss.extensions.api.node.AttrValue;
import ro.sync.ecss.extensions.api.node.AuthorDocumentFragment;
import ro.sync.ecss.extensions.api.node.AuthorElement;
import ro.sync.ecss.extensions.api.node.AuthorNode;

import javax.swing.text.BadLocationException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.*;

public class MOFDocumentFilter extends AuthorDocumentFilter {

    private final AuthorAccess authorAccess;
    private final AuthorDocumentController controller;

    private static Logger logger = Logger.getLogger(MOFDocumentFilter.class.getName());
    private ArrayList<String> IdInDocument;
    private ArrayList<String> IdWasInDocument;
    private int maxIdValue = 0;
    private List<String> lostElements;

    private class SortByIntValue implements Comparator<String>{

        public int compare(String o1, String o2) {
            Integer i1 = Integer.valueOf(o1);
            Integer i2 = Integer.valueOf(o2);
            return o1.compareTo(o2);
    }
}

    public MOFDocumentFilter(AuthorAccess access) {
        this.authorAccess = access;
        this.controller = access.getDocumentController();
        this.IdInDocument = new ArrayList<String>();
        this.lostElements = new ArrayList<String>();
        this.lostElements.add("метаданные");
        this.lostElements.add("редакции");
        this.lostElements.add("ссылки");

        try{
            AuthorNode[] nodes = authorAccess.getDocumentController().findNodesByXPath("//*[@id and not(ancestor::метаданные) and not(self::редакции) and not(self::ссылки)]", true, true, true);
            for (AuthorNode node : nodes){
                try{
                    if (node.getType() == AuthorNode.NODE_TYPE_ELEMENT){
                        AuthorElement ae = (AuthorElement) node;
                        String texValue = ae.getAttribute("id").getValue();
                        int id = Integer.valueOf(texValue);
                        IdInDocument.add(texValue);
                        if (id > maxIdValue)
                            maxIdValue = id;
                    }
                } catch (Exception e){
                    // do nothing
                }
            }

        } catch (Exception e){
            authorAccess.getWorkspaceAccess().showErrorMessage("error:" + e.getMessage());
        }

        Collections.sort(IdInDocument, new SortByIntValue());
//        String currentItem = IdInDocument.get(0);
//        String items = "";
//        for (int i = 1; i < IdInDocument.size(); i++){
//            if (currentItem == IdInDocument.get(i))
//                items += currentItem + ",";
//            currentItem = IdInDocument.get(i);
//        }

//        authorAccess.getWorkspaceAccess().showErrorMessage("doubled id's: " + items);

        this.IdWasInDocument = new ArrayList<String>(this.IdInDocument);
        Collections.copy(this.IdWasInDocument, this.IdInDocument);

    }

    private boolean isStrictModeActivated() {
        String strictMode = authorAccess.getOptionsStorage().getOption("strictMode", "false");
        return "true".equals(strictMode);
    }

    public boolean delete(AuthorDocumentFilterBypass filterBypass, int startOffset, int endOffset, boolean withBackspace){

        try {
            AuthorDocumentFragment fragment = controller.createDocumentFragment(startOffset, endOffset);
            String xmlFragment = controller.serializeFragmentToXML(fragment);

            XMLInputFactory factory = XMLInputFactory.newInstance();
            XMLStreamReader streamReader = factory.createXMLStreamReader(new StringReader("<root>" + xmlFragment + "</root>"));
            while(streamReader.hasNext()){
                streamReader.next();
                if (streamReader.getEventType() == XMLStreamReader.START_ELEMENT) {
                    for (int i = 0; i < streamReader.getAttributeCount(); i++) {
                        String name = streamReader.getAttributeName(i).toString();
                        if (name.equalsIgnoreCase("id")) {
                            String value = streamReader.getAttributeValue(i);
                            this.IdInDocument.remove(value);
                        }
                    }
                }
            }
            filterBypass.delete(startOffset,endOffset,withBackspace);
        } catch (BadLocationException e) {
            return false;
        } catch (Exception e){
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(baos);
            e.printStackTrace(ps);
            try {
                String content = baos.toString("UTF8");
                authorAccess.getWorkspaceAccess().showErrorMessage(e.getLocalizedMessage() + "\n\n" + content);
                return false;
            } catch (UnsupportedEncodingException e1) {
                authorAccess.getWorkspaceAccess().showErrorMessage(e1.getMessage());
            }
        }
        return true;
    }

    public boolean deleteNode(AuthorDocumentFilterBypass filterBypass, AuthorNode node){

        try {
            AuthorDocumentFragment fragment = controller.createDocumentFragment(node, true);
            unUseAllId(fragment.getContentNodes());
            filterBypass.deleteNode(node);
        } catch (BadLocationException e) {
            authorAccess.getWorkspaceAccess().showErrorMessage(e.getLocalizedMessage());
            return false;
        }

        return true;
    }

    public boolean insertNode(AuthorDocumentFilterBypass filterBypass,
                              int caretOffset, AuthorNode element) {

        try {
            AuthorDocumentFragment fragment = controller.createDocumentFragment(element, true);
            fillID(fragment.getContentNodes());
            filterBypass.insertNode(caretOffset,fragment.getContentNodes().get(0));
        } catch (BadLocationException e) {
            authorAccess.getWorkspaceAccess().showErrorMessage(e.getLocalizedMessage());
        }

        return super.insertNode(filterBypass, caretOffset, element);
    }

    public void insertText(AuthorDocumentFilterBypass filterBypass, int caretOffset,
                           String toInsert) {
        super.insertText(filterBypass, caretOffset, toInsert);

    }

    public void insertFragment(AuthorDocumentFilterBypass arg0, int arg1,
                               AuthorDocumentFragment arg2) {
        fillID(arg2.getContentNodes());
        super.insertFragment(arg0, arg1, arg2);

    }

    private String getIdValue(){
        this.maxIdValue++;
        String value = "" + this.maxIdValue;
        this.IdInDocument.add(value);
        return value;
    }

    private void fillID(List<AuthorNode> nodes){
        for (AuthorNode node : nodes){
            if (!lostElements.contains(node.getName())){
                AuthorElement ae = (AuthorElement) node;
                if (!node.getName().equals("документ"))
                    try {
                        String idValue = ae.getAttribute("id").getValue();
                        if (!this.IdWasInDocument.contains(idValue) || this.IdInDocument.contains(idValue))
                            ae.setAttribute("id", new AttrValue("" + getIdValue()));
                    } catch (Exception e) {
                        ae.setAttribute("id", new AttrValue("" + getIdValue()));
                    }
                fillID(ae.getContentNodes());
            }
        }
    }

    private void unUseAllId(List<AuthorNode> nodes){
        for (AuthorNode currentNode : nodes){
            AuthorElement ae = (AuthorElement) currentNode;
            try{
                String idValue = ae.getAttribute("id").getValue();
                this.IdInDocument.remove(idValue);
            } catch (Exception e){

            }
            unUseAllId(ae.getContentNodes());
        }
    }
}
